﻿#include "CConfig.h"
#include "../Globals.h"
#include <string>
#include "../../third/tinytoml/toml.h"
#include <windows.h>      
#include <shlobj.h>  
#include <shlguid.h> 
#include <map>

#include "../IpcClient.h"

#define RGB(r,g,b)   ((COLORREF)(((BYTE)(r)|((WORD)((BYTE)(g))<<8))|(((DWORD)(BYTE)(b))<<16)))
#define CONFIG_DIR  L"server.lnk"
#define CONFIG_FILE  L"input_config.toml"
#define SERVER_EXE_NAME L"MengYuServer.exe"


COLORREF CConfig::HeaderTextColor = RGB(0, 0, 0); //候选词窗口的标题栏文字颜色
COLORREF CConfig::HeaderBackgroundColor = RGB(0xf0, 0xf0, 0xf0);//候选词窗口的标题栏背景颜色
COLORREF CConfig::CandTextColor = RGB(0, 0, 0); //候选词窗口的文字颜色
COLORREF CConfig::CandBackgroundColor = RGB(0xf0, 0xf0, 0xf0); //候选词窗口的背景颜色
COLORREF CConfig::CandSelectedTextColor = RGB(0xf0, 0xf0, 0xf0);; //候选词窗口的选中文字颜色
COLORREF CConfig::CandSelectedBackgroundColor = RGB(0x65, 0x4b, 0xe5); //候选词窗口的选中背景颜色
COLORREF CConfig::NumberColor = RGB(128, 128, 128); //候选词窗口的序号颜色
COLORREF CConfig::NumberBackgroundColor = RGB(0xff, 0xff, 0xff); //候选词窗口的序号颜色
COLORREF CConfig::BorderColor = RGB(0, 0, 0); //边框颜色

bool CConfig::EnableHistory = true; //是否启用历史记录

unsigned int CConfig::BorderWidth = 1; //边框宽度
unsigned int CConfig::MaxCandidates = 3; //最大候选词数量 <9
unsigned int CConfig::FontHeight = 33; //字体高度
unsigned int CConfig::FontSize = 13; //字体大小
WCHAR CConfig::FontName[50] = L"Microsoft YaHei UI";

std::wstring CConfig::ServerDir; //服务端目录


std::map<wchar_t, std::wstring> s_PunctuationTable = {
    {L'!',  L"！"},
    {L'$',  L"￥"},
    {L'&',  L"＆"},
    {L'(',  L"（"},
    {L')',  L"）"},
    {L',',  L"，"},
    {L'.',  L"。"},
    {L':',  L"："},
    {L';',  L"；"},
    {L'?',  L"？"},
    {L'@',  L"＠"},
    {L'\\', L"、"},
    {L'^',  L"＾"},
    {L'_',  L"——"},
    {L'[',  L"【"},
    {L']',  L"】"},
    {L'<',  L"《"},
    {L'>',  L"》"},
    {L'{',  L"「"},
    {L'}',  L"」"},
}; //标点符号

//快捷方式解析函数
static std::wstring resolveShortcut(const std::wstring& shortcutPath) {
    CoInitialize(NULL);
    IShellLink* pShellLink = NULL;
    wchar_t realPath[MAX_PATH] = {0};
    HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, 
        CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&pShellLink);
    
    if (SUCCEEDED(hres)) {
        IPersistFile* pPersistFile;
        hres = pShellLink->QueryInterface(IID_IPersistFile, (void**)&pPersistFile);
        if (SUCCEEDED(hres)) {
            std::wstring wsShortcut(shortcutPath.begin(), shortcutPath.end());
            hres = pPersistFile->Load(wsShortcut.c_str(), STGM_READ);
            if (SUCCEEDED(hres)) {
                pShellLink->GetPath(realPath, MAX_PATH, NULL, SLGP_SHORTPATH);
            }
            pPersistFile->Release();
        }
        pShellLink->Release();
    }
    CoUninitialize();
    return realPath; // 解析失败返回空
}


/// @brief  读取int值，并且将其赋值给out_value
/// @tparam T 
/// @param candidate_config 
/// @param name 变量名
/// @param out_value 
template<typename T>
static bool _ReadIntValue(const toml::Value* candidate_config, const std::string& name, T& out_value) {
    auto val = candidate_config->find(name);
    if ( val && val->is<int64_t>()) {
        out_value =  (T)(val->as<int64_t>());
        std::wstring str = std::wstring(name.begin(),name.end()) + L": " + std::to_wstring(out_value);
        IPC_LOG(L"Read config: " + str);
        return true;
    }
    return false;
}

static bool _ReadColorValue(const toml::Value* candidate_config, const std::string& name, COLORREF& out_value) {
    bool ret = _ReadIntValue(candidate_config, name, out_value);
    if(!ret) return false;
    COLORREF color = ((out_value & 0x00ff0000) >> 16) | (out_value & 0x0000FF00) | ((out_value & 0x000000ff) << 16); 
    out_value = color;
    return true;
}

static bool _ReadBoolValue(const toml::Value* candidate_config, const std::string& name, bool& out_value) {
    auto val = candidate_config->find(name);
    if ( val && val->is<bool>()) {
        out_value =  val->as<bool>();
        return true;
    }
    return true;
}

#define readIntValue(name) _ReadIntValue(candidate_config, #name, name)
#define readColorValue(name) _ReadColorValue(candidate_config, #name, name)
#define readBoolValue(name) _ReadBoolValue(candidate_config, #name, name)


void CConfig::LoadConfig(){
    WCHAR wszFileName[MAX_PATH] = {'\0'};
    DWORD cchA = GetModuleFileName(Global::dllInstanceHandle, wszFileName, ARRAYSIZE(wszFileName));
    size_t iDicFileNameLen = cchA + wcslen(CONFIG_FILE);

    std::wstring configFile;
    
    // find the last '/'
    while (cchA--)
    {
        WCHAR wszChar = wszFileName[cchA];
        if (wszChar == '\\' || wszChar == '/')
        {
            configFile.assign(wszFileName, cchA + 1);
            configFile.append(CONFIG_DIR);
            break;
        }
    }

    if(configFile.empty()){
        return;
    }

    ServerDir = resolveShortcut(configFile);
    if (ServerDir.empty()) {
        return;
    }

    std::ifstream ifs( ServerDir + L"\\input_config.toml");
    if (!ifs) {
        return;
    }
    do{
        toml::ParseResult pr = toml::parse(ifs);

        if (!pr.valid()) {
            std::cout << pr.errorReason << std::endl;
            break;
        }
    
        // pr.value存储了解析后的值。
        const auto& root = pr.value;
        const auto candidate_config = root.find("CandidateConfig");
        if(candidate_config == NULL){
            break;
        }
    
        // 读取所有配置项
        readColorValue(HeaderBackgroundColor);
        readColorValue(HeaderTextColor);
        readColorValue(CandTextColor);
        readColorValue(CandBackgroundColor);
        readColorValue(CandSelectedTextColor); 
        readColorValue(CandSelectedBackgroundColor);
        readColorValue(NumberColor);
        readColorValue(NumberBackgroundColor);
        readColorValue(BorderColor);

        readBoolValue(EnableHistory);
        
        readIntValue(BorderWidth);
        readIntValue(MaxCandidates);
        readIntValue(FontHeight);
        readIntValue(FontSize);


        // 标点符号表读取
        auto punctuation = candidate_config->find("PunctuationTable");
        if (!punctuation || !punctuation->is<toml::Array>()) {
            break;
        }

        for (const auto& entry : punctuation->as<toml::Array>()) {
            if (!entry.is<toml::Array>() || entry.size() != 2) {
                continue;
            }
            
            const auto en = entry.find(0);
            const auto cn = entry.find(1);
            if (!en || !cn || !en->is<std::string>() || !cn->is<std::string>()) {
                continue;
            }
            
            std::string en_str = en->as<std::string>();
            std::string cn_str = cn->as<std::string>();
            if(en_str.size() != 1 || cn_str.empty()){
                continue;
            }

            wchar_t wstr[256];
            wchar_t code;
            // UTF-8转Unicode
            int wlen = MultiByteToWideChar(CP_UTF8, 0, en_str.data(), 1, &code, 1);
            if(wlen != 1){
                continue;
            }
            wlen = MultiByteToWideChar(CP_UTF8, 0, cn_str.data(), cn_str.size(), wstr, sizeof(wstr)/sizeof(wstr[0]));
            if(wlen > 0){
                s_PunctuationTable[code] = std::wstring(wstr,wlen);
            }
        }
    }while(0);
   
    ifs.close();
}


const wchar_t* CConfig::GetPunctuation(wchar_t c,DWORD &out_size){
    auto it = s_PunctuationTable.find(c);
    if(it == s_PunctuationTable.end()){
        out_size = 0;
        return nullptr;
    }

    out_size = (DWORD) it->second.size();
    return it->second.data();
}